﻿using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Linq;
using System.Net;
using System.Net.Http;
using Xunit;
using System.Text;
using System.Net.Http.Headers;

namespace SovaWebService.Tests
{
    public class WebServiceTests
    {
        private const string ApiUrl = "https://localhost:5001/api/";
        private const string QuestionsApiBaseUrl = ApiUrl + "questions";
        private const string AuthorsApiBaseUrl = ApiUrl + "authors";
        private const string TagsApiBaseUrl = ApiUrl + "tags";
        private const string AccountsApiBaseUrl = ApiUrl + "accounts";
        private const string MarkedQuestionsBaseUrl = ApiUrl + "marked_questions";
        private const string WeightedWordsBaseUrl = ApiUrl + "weighted_words";


        // Questions
        [Fact]
        public void ApiQuestions_GetWithNoArguments_OkAndAllQuestions()
        {
            //Given
            var (data, statusCode) = GetObject(QuestionsApiBaseUrl);
            //Then
            Assert.Equal(HttpStatusCode.OK, statusCode);

            Assert.Equal(2198, data["total"].Value<int>());
            Assert.Equal("What is the fastest way to get the value of π?", data["questions"].First["title"].Value<string>());
            Assert.Equal("Is there a business reason for striving for pure CSS layout?", data["questions"].Last["title"].Value<string>());
        }

        [Fact]
        public void ApiQuestions_GetWithValidQuestionId_OkAndQuestion()
        {
            //Given
            var (question, statusCode) = GetObject(QuestionsApiBaseUrl + "/19");
            //Then
            Assert.Equal(HttpStatusCode.OK, statusCode);
            Assert.Equal(164, question["score"]);
        }

        [Fact]
        public void ApiQuestions_GetWithInvalidQuestionId_NotFound()
        {
            //Given
            var (question, statusCode) = GetObject(QuestionsApiBaseUrl + "/1");
            //Then
            Assert.Equal(HttpStatusCode.NotFound, statusCode);
        }

        //Answers

        //Tags

        [Fact]
        public void ApiAccounts_CreateWithMissingData_BadRequest()
        {
            //Given
            var data = new
            {
                Name = "testy"
            };
            //When
            var (account, statusCode) = PostData(AccountsApiBaseUrl, data);
            //Then
            Assert.Equal(HttpStatusCode.BadRequest, statusCode);
        }

        [Fact]
        public void ApiAccounts_CreateWithValidData_Created()
        {
            //Given
            var data = new
            {
                name = "test testovic",
                email = "testovic@test.com",
                password = "test123"
            };
            var loginData = new
            {
                email = "testovic@test.com",
                password = "test123"
            };
            //When
            var (account, statusCode) = PostData(AccountsApiBaseUrl, data);
            var (loginResponse, loginStatusCode) = PostData(AccountsApiBaseUrl + "/login", loginData);
            string token = loginResponse["token"].ToString();
            //Then
            Assert.Equal(HttpStatusCode.Created, statusCode);
            Assert.Equal("test testovic", account["name"]);
            Assert.Equal("testovic@test.com", account["email"]);
            Assert.NotNull(account["id"]);
            //Cleanup
            DeleteData(AccountsApiBaseUrl, (int)account["id"], token);
        }

        [Fact]
        public void ApiAccounts_Delete_Unauthorized()
        {
            //When
            HttpStatusCode statusCode = DeleteData(AccountsApiBaseUrl, 0);
            //Then
            Assert.Equal(HttpStatusCode.Unauthorized, statusCode);
        }

        [Fact]
        public void ApiAccounts_DeleteWithValidId_Ok()
        {
            //Given
            var data = new
            {
                name = "tasty test",
                email = "tasty@test.com",
                password = "tasty"
            };
            var (createdAccount, createStatusCode) = PostData(AccountsApiBaseUrl, data);
            var loginData = new
            {
                email = "tasty@test.com",
                password = "tasty"
            };
            var (loginResponse, loginStatusCode) = PostData(AccountsApiBaseUrl + "/login", loginData);
            string token = loginResponse["token"].ToString();
            //When
            HttpStatusCode deleteStatusCode = DeleteData(AccountsApiBaseUrl, (int)createdAccount["id"], token);
            var (account, statusCode) = GetObject(AccountsApiBaseUrl, token);
            //Then
            Assert.Equal(HttpStatusCode.OK, deleteStatusCode);
            Assert.Equal(HttpStatusCode.NotFound, statusCode);
            //Cleanup
            DeleteData(AccountsApiBaseUrl, (int)createdAccount["id"]);
        }

        [Fact]
        public void ApiMarkedQuestions_MarkWithValidData_Ok()
        {
            //Given
            var registrationData = new
            {
                name = "test test",
                email = "test@test.com",
                password = "test123"
            };
            var (regResponse, regStatusCode) = PostData(AccountsApiBaseUrl, registrationData);
            var loginData = new
            {
                email = "test@test.com",
                password = "test123"
            };
            var (loginResponse, loginStatusCode) = PostData(AccountsApiBaseUrl + "/login", loginData);
            string token = loginResponse["token"].ToString();
            //When
            var markQuestionData = new
            {
                questionId = 2129693,
                annotation = "TEST This was a very interesting insight indeed, love this TEST!"
            };
            var (markQuestionResponse, markQuestionStatusCode) = PostData(MarkedQuestionsBaseUrl, markQuestionData, token);
            var (markedQuestions, getMarkedQuestionsStatusCode) = GetObject(MarkedQuestionsBaseUrl, token);
            //Then
            Assert.Equal(HttpStatusCode.Created, markQuestionStatusCode);
            Assert.Equal(HttpStatusCode.OK, getMarkedQuestionsStatusCode);
            Assert.Equal(1, (int)markedQuestions["total"]);
            //Cleanup
            DeleteData(MarkedQuestionsBaseUrl, 2129693, token);
            DeleteData(AccountsApiBaseUrl, (int)regResponse["id"], token);
        }

        [Fact]
        public void ApiWeightedWords_GetWeightedWordWithValidTerm()
        {
            //Given
            var registrationData = new
            {
                name = "test test",
                email = "test@test.com",
                password = "test123"
            };
            var (regResponse, regStatusCode) = PostData(AccountsApiBaseUrl, registrationData);
            var loginData = new
            {
                email = "test@test.com",
                password = "test123"
            };
            var (loginResponse, loginStatusCode) = PostData(AccountsApiBaseUrl + "/login", loginData);
            string token = loginResponse["token"].ToString();
            //When
            var (weightedWords, statusCode) = GetObject(WeightedWordsBaseUrl + "/visual+studio", token);
            //Then
            Assert.Equal(HttpStatusCode.OK, statusCode);
            Assert.Equal("visual", weightedWords["words"].First()["word"]);
        }

        // HELPER METHODS.

        (JArray, HttpStatusCode) GetArray(string url)
        {
            var client = new HttpClient();
            var response = client.GetAsync(url).Result;
            var data = response.Content.ReadAsStringAsync().Result;
            return ((JArray)JsonConvert.DeserializeObject(data), response.StatusCode);
        }

        (JObject, HttpStatusCode) GetObject(string url, string jwt = "")
        {
            var client = new HttpClient();

            if (!string.IsNullOrEmpty(jwt))
            {
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwt);
            }

            var response = client.GetAsync(url).Result;
            var data = response.Content.ReadAsStringAsync().Result;
            return ((JObject)JsonConvert.DeserializeObject(data), response.StatusCode);
        }

        (JObject, HttpStatusCode) PostData(string url, object content, string jwt = "")
        {
            var client = new HttpClient();
            var requestContent = new StringContent(
                JsonConvert.SerializeObject(content),
                Encoding.UTF8,
                "application/json");

            if (!string.IsNullOrEmpty(jwt))
            {
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwt);
            }

            var response = client.PostAsync(url, requestContent).Result;
            var data = response.Content.ReadAsStringAsync().Result;
            return ((JObject)JsonConvert.DeserializeObject(data), response.StatusCode);
        }

        HttpStatusCode DeleteData(string url, int id, string jwt = "")
        {
            var client = new HttpClient();

            if (!string.IsNullOrEmpty(jwt))
            {
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwt);
            }

            var response = client.DeleteAsync(url + "/" + id).Result;
            return response.StatusCode;
        }
    }
}

